package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SearchService;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {
    //使用springdataelasticsearch技术进行全文检索
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Override
    public Map searchQuery(Map<String, String> searchMap) {
        Map<String, Object> resultMap = new HashMap<>();
        //第一个功能，根据关键字搜索
        if (searchMap != null) {
            //1创建执行搜索的builder对象
            NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
            //2构建搜索条件对象
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

            //2.1按照关键字查询
            if (StringUtils.isNotEmpty(searchMap.get("keywords"))) {//有搜索关键字
                //matchQuery表示模糊匹配，参数1表示域名
                boolQueryBuilder.must(QueryBuilders.matchQuery("name", searchMap.get("keywords")).operator(Operator.AND));
            }

            //2.2品牌过滤查询条件
            if (StringUtils.isNotEmpty(searchMap.get("brand"))) {
                boolQueryBuilder.filter(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
            }

            //2.3规格过滤条件,spec_屏幕尺寸,索引库的存储格式：spechMap.规格名字.keyword
            for (String key : searchMap.keySet()) {
                if (key.startsWith("spec_")) {//说明是规格参数
                    String value = searchMap.get(key);
                    //域名需要拼接specMap.屏幕尺寸.keyword
                    boolQueryBuilder.filter(QueryBuilders.termQuery("specMap." + key.substring(5) + ".keyword", value));
                }
            }

            //2.4 价格区间过滤条件
            if (StringUtils.isNotEmpty(searchMap.get("price"))) {//有价格关键字
                String[] prices = searchMap.get("price").split("-");
                if (prices.length == 2) {//3000以下的价格
                    boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lt(prices[1]));//小于第二个数字
                }
                //否则大于第一个数字
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gt(prices[0]));
            }


            //3.添加查询条件
            nativeSearchQueryBuilder.withQuery(boolQueryBuilder);

            //3.1 品牌聚合查询
            //定义品牌聚合名称,目的是为了根据skuBrand去查询,terms("skuBrand")品牌分类之后的列名，field("brandName")作用域
            String skuBrand = "skuBrand";
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(skuBrand).field("brandName").size(10));

            //3.2 规格聚合查询
            //定义规格聚合名称,目的是为了根据skuSpec去查询
            String skuSpec = "skuSpec";
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(skuSpec).field("spec.keyword").size(10));

            //3.3 分页查询
            String pageNum = searchMap.get("pageNum");
            String pageSize = searchMap.get("pageSize");
            if (StringUtils.isEmpty(pageNum)) {
                pageNum = "1";
            }
            if (StringUtils.isEmpty(pageSize)) {
                pageSize = "60";
            }
            nativeSearchQueryBuilder.withPageable(PageRequest.of(Integer.parseInt(pageNum) - 1, Integer.parseInt(pageSize)));

        /*3.4 排序查询
           sortFiled表示排序字段域，sortRule表示排序规则，ASC和DESC
        */
            if (StringUtils.isNotEmpty((String) searchMap.get("sortField")) && StringUtils.isNotEmpty((String) searchMap.get("sortRule"))) {
                if ("ASC".equalsIgnoreCase((String) searchMap.get("sortRule"))) {//升序
                    nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort((String) searchMap.get("sortField")).order(SortOrder.ASC));
                } else if ("DESC".equalsIgnoreCase((String) searchMap.get("sortRule"))) {
                    nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort((String) searchMap.get("sortField")).order(SortOrder.DESC));
                }
            }

            //3.5 高亮显示
            HighlightBuilder.Field field=new HighlightBuilder
                    .Field("name")
                    .preTags("<span style='color:red'>")
                    .postTags("</span>");
            nativeSearchQueryBuilder.withHighlightFields(field);

                /**
                 *
                 */
                //4.执行查询，返回结果
                AggregatedPage<SkuInfo> skuInfos = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
                    @Override
                    public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                        //查询结果，操作
                        //存放查询的结果封装
                        List<T> list = new ArrayList<>();
                        //获取结果
                        SearchHits hits = searchResponse.getHits();
                        if (hits != null) {//说明有结果
                            //遍历，重新封装为SkuInfo对象
                            for (SearchHit hit : hits) {
                                SkuInfo skuInfo = JSON.parseObject(hit.getSourceAsString(), SkuInfo.class);
                                //高亮设置
                                Map<String, HighlightField> fieldMap = hit.getHighlightFields();
                                if(fieldMap!=null && fieldMap.size()>0){
                                    skuInfo.setName(fieldMap.get("name").getFragments()[0].toString());
                                }
                                list.add((T) skuInfo);
                            }
                        }
                        /**
                         * 参数1表示搜索结果list
                         * 参数2表示分页对象
                         * 参数3表示获取命中结果总记录数
                         * 参数4表示响应的聚合查询
                         */

                        return new AggregatedPageImpl<T>(list, pageable, hits.getTotalHits(), searchResponse.getAggregations());
                    }
                });


                //5.数据封装
                resultMap.put("total", skuInfos.getTotalElements());//总记录数
                resultMap.put("totalPages", skuInfos.getTotalPages());//总页数
                resultMap.put("skuList", skuInfos.getContent());//数据列表

                //获取品牌聚合查询结果集并且进行封装
                StringTerms brandTerms = (StringTerms) skuInfos.getAggregation(skuBrand);
                List<String> brandList = brandTerms.getBuckets().stream().map(bucket -> bucket.getKeyAsString()).collect(Collectors.toList());
                resultMap.put("brandList", brandList);

                //获取规格聚合查询结果集并且进行封装
                StringTerms specTerms = (StringTerms) skuInfos.getAggregation(skuSpec);
                List<String> specList = specTerms.getBuckets().stream().map(bucket -> bucket.getKeyAsString()).collect(Collectors.toList());
                resultMap.put("specList", this.specFormate(specList));

                //封装当前页信息
                resultMap.put("pageNum", pageNum);


            }

            return resultMap;
        }


    //转化规格格式
    public Map<String,Set<String>> specFormate(List<String> specList){
        //1.定义一个map集合用来封装转化之后的规格
        Map<String,Set<String>> resultMap=new HashMap<>();
        if(specList!=null && specList.size()>0){
            for (String specJsonString : specList) {//spec="{'颜色': '蓝色', '版本': '6GB+128GB'}"
                //把json字符串转化成map
                Map<String,String>specMap = JSON.parseObject(specJsonString, Map.class);
                for (String specKey : specMap.keySet()) {
                    Set<String> specSet = resultMap.get(specKey);
                    if(specSet==null){
                        specSet=new HashSet<>();
                    }
                    specSet.add(specMap.get(specKey));
                    //将set封装到map中
                    resultMap.put(specKey,specSet);
                }
            }
        }
        return resultMap;
    }

    }

